﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using Xant.FlowEngine.Interface;
using Xant.FlowEngine.Core;
using Xant.FlowEngine.Factory;
using Xant.FlowEngine.Utility;
using Dapper;
using Autofac;

namespace Xant.FlowEngine
{
    public sealed class ProcessManager
    {
        private static ProcessManager manager;
        private Dictionary<Guid, ProcessDispatcher> dispatchers;
        private IOrganizationLocator organzLocator;

        public ProcessManager(IOrganizationLocator organzLocator)
        {
            dispatchers = new Dictionary<Guid, ProcessDispatcher>();
            this.organzLocator = organzLocator;
        }

        public static ProcessManager Manager
        {
            get
            {
                if (manager == null)
                {
                    var builder = new ContainerBuilder();
                    builder.RegisterType<ProcessManager>().SingleInstance();
                    builder.RegisterModule(new Autofac.Configuration.ConfigurationSettingsReader("autofac"));
                    using (var container = builder.Build())
                    {
                        manager = container.Resolve<ProcessManager>();
                    }
                }
                return manager;
            }
        }

        #region 事件处理
        /*
        public void process_StatusChanged(ProcessInstance process)
        {

        }

        public void activity_StatusChanged(AbstractActivity activity)
        {

        }

        public void workitem_AfterAction(AbstractWorkitem workitem)
        {

        }
        */
        #endregion

        public ProcessInstance CreateProcessInstance(BizDataBag bag, Guid templateId)//, attachFile file)
        {
            ProcessTemplate template = null;
            return CreateProcessInstance(bag, template);
        }

        public ProcessInstance CreateProcessInstance(BizDataBag bag, ProcessTemplate template)
        {
            if(bag==null)
                throw new ArgumentNullException("bag");
            if(template==null)
                throw new ArgumentNullException("template");
            var process = ProcessFactory.CreateProcess(template);
            //将新创建的流程实例序列化到数据库中保存
            SerializerManager.SaveProcess(process);
            //绑定事件响应函数，以便处理数据序列化逻辑
            process.StatusChanged += SerializerManager.OnProcessStatusChanged;
            foreach (var activity in process.Activities)
            {
                activity.StatusChanged += SerializerManager.OnActivityStatusChanged;
                if (activity.GetType().IsSubclassOf(typeof(ManualActivity)))
                {
                    var manual = activity as ManualActivity;
                    manual.WorkitemInitialized += SerializerManager.OnWorkitemInitialized;
                }
            }
            return process;
        }

        public SubProcess CreateSubProcessInstance()
        {
            throw new NotImplementedException();
        }

        public bool ProcessInMemory(Guid processId)
        {
            return dispatchers.ContainsKey(processId);
        }

        public void Run<T>(T process) where T : ProcessInstance
        {
            if(dispatchers.ContainsKey(process.Id))
            {
                throw new ApplicationException("流程已经在运行中，不要重复启动流程！");
            }
            var dispatcher = new ProcessDispatcher(process);
            dispatchers.Add(process.Id, dispatcher);
            dispatcher.Start();
        }

        /// <summary>
        /// 重新加载流程实例到流程池中
        /// </summary>
        /// <param name="processId">流程主键</param>
        /// <param name="force">是否强制加载，为真时不管该流程是否已加载到池中都会重新从数据库加载，为否时仅当该流程尚加载时才从数据库加载</param>
        public void ReloadProcess(Guid processId, bool force=false)
        {
            ProcessDispatcher dispatcher = dispatchers[processId];
            if(dispatcher==null || force)
            {
                //重新从数据库中加载流程
            }
            if (dispatcher == null)
            {
                throw new ApplicationException("流程加载失败。");
            }
        }

        public void GetWorkitemOwnerInfo(Guid workitemId, out Guid ownerActivityId, out Guid ownerProcessId)
        {
            using (IDbConnection conn = DBConnectionManager.GetConnection())
            {
                var rlt = conn.Query("SELECT W.ActivityId, A.ProcessId FROM xfWorkitem W JOIN xfActivity A ON A.Id=W.ActivityId WHERE W.Id=@id", new { Id = workitemId });
                ownerActivityId = rlt.First().ActivityId;
                ownerProcessId = rlt.First().ProcessId;
            }
        }

        public Workitem LoadWorkitem(Guid workitemId)
        {
            Guid activityId, processId;
            GetWorkitemOwnerInfo(workitemId, out activityId, out processId);
            ReloadProcess(processId);
            var dispatcher = dispatchers[processId];
            var activity = dispatcher.Process.Activities.Where(p => p.Id.Equals(activityId)).Single() as ManualActivity;
            return activity.Workitems.Where(p => p.Id.Equals(workitemId)).Single();
        }

        public void WorkitemRead(Guid workitemId, String opinion)
        {

        }

        public void WorkitemPass(Guid workitemId, String opinion)
        {
            var workitem = LoadWorkitem(workitemId);
            workitem.Pass(DateTime.Now, null);
        }

        public void WorkitemRetreat(Guid workitemId, String opinion)
        {

        }

        public void WorkitemTerminate(Guid workitemId, String opinion)
        {

        }


        public IEnumerable<AbstractActivity> GetRunningActivities(Guid processId)
        {
            ProcessDispatcher dispatcher = dispatchers[processId];
            if (dispatcher == null)
            {
                throw new ApplicationException("该流程未加载到内存中，请先启动该流程。流程主键："+processId.ToString());
            }
            return dispatcher.RunningActivities;
        }
    }
}
